IDIAL Kubernetes-Deployment: Interne PKI + interne Datenbank (SQLite)
Diese Anleitung beschreibt das Deployment von IDIAL Core, IDIAL App/UI und
dem internen PKI-Stack (Nexus Certificate Manager) auf einem
Kubernetes-Cluster mittels reiner kubectl apply -f-Manifeste. Sie richtet
sich an Administratoren, die IDIAL in einer Testumgebung oder einem
abgegrenzten Kunden-Cluster ohne externe Datenbank und ohne externe PKI
betreiben wollen.
1. Übersicht
1.1 Was dieses Setup abdeckt
- IDIAL Core — mit eingebetteter SQLite-Datenbank (kein externer Datenbankserver erforderlich).
- IDIAL App/UI — Web-Frontend, kommuniziert ausschließlich mit Core.
- Interner PKI-Stack (Nexus Certificate Manager) — PostgreSQL,
Artefakt-/WebDAV-Server,
cis-server,cf-server,pgw(Protocol Gateway). Vollständig im Cluster betrieben, keine externe Zertifizierungsstelle notwendig.
Für Umgebungen mit einer bereits vorhandenen, extern betriebenen PostgreSQL-Instanz und/oder einer externen PKI siehe das separate Setup "Externe PostgreSQL + externe PKI".
1.2 Architektur
Alle Komponenten laufen in einem einzigen Namespace (idial) und
kommunizieren über Cluster-interne Kubernetes-Services (Standard-ClusterIP,
kein Multus/macvlan, keine dedizierte OT-Netzwerksegmentierung):
| Komponente | Service-Name | Port | Beschreibung |
|---|---|---|---|
| IDIAL Core | idial | 5000 | REST-API |
| IDIAL App/UI | idial-web-backend | 5555 | Web-Oberfläche |
| PKI-Postgres | db | 5432 | Datenbank des Nexus-CM-Stacks |
| PKI-Artefaktserver | files | 8000 | WebDAV-Server für PKI-Bootstrap |
| Nexus CIS | cis-server | 1234 | Certificate Issuing Service |
| Nexus CF | cf-server | 5009 | Certificate Factory |
| Protocol Gateway | pgw | 8080/8443/8444 | Geräte-Schnittstelle des PKI-Stacks |
1.3 Cluster-Voraussetzungen
- Kubernetes-Cluster mit containerd als Container-Runtime und Calico (oder einer vergleichbaren CNI) — getestet mit einem kubeadm-Cluster (1 Control-Plane, ≥1 Worker).
- Kein Registry-Zugriff vorausgesetzt: alle Images werden per Tarball auf die Nodes importiert (siehe Abschnitt 2).
- Ein bereits installierter ingress-nginx-Controller (
IngressClass: nginx), falls externer Zugriff über einen Browser benötigt wird (Abschnitt 5). Ohne ingress-nginx ist IDIAL nur cluster-intern erreichbar. - Eine StorageClass mit dynamischer Provisionierung (z. B.
local-path-provisioner). Bei node-lokalem Storage auf einem Multi-Node-Cluster: siehe Hinweis in Abschnitt 3.4 zunodeSelector.
2. Vorbereitung
2.1 Benötigte Images
| Image | Verwendung |
|---|---|
docker.io/bxc2security/idial:v0.38.16-dev | IDIAL Core |
docker.io/bxc2security/idial-web-backend:v0.42.8 | IDIAL App/UI |
docker.io/bxc2security/idial-db-schema:1.7.0 | Schema-Install (initContainer auf Core) |
docker.io/postgres:18-alpine | PKI-Postgres (db) + SQL-Schema-Job |
docker.io/bxc2security/files:latest | Interner Artefakt-/WebDAV-Server |
docker.io/bxc2security/nexus:cf-server-8.14.0 | cis-server + cf-server |
docker.io/bxc2security/nexus:pgw-8.14.0 | pgw (Protocol Gateway) |
docker.io/alpine:latest | Wait-Loop-InitContainer, Download-Schritte |
docker.io/bxc2security/alpine:latest | Config-Editor (eigenes Rebuild mit openssl+keytool) |
Die Versionen für Core/App/Schema/CM sind zentral in 02-configmap.yaml
gepflegt. Bei einem Versionswechsel müssen zusätzlich die
Image-Referenzen in den betroffenen Deployment-/Job-Manifesten
(04-, 13-, 14-, 15-*.yaml) angepasst werden.
2.2 Image-Import ohne Registry-Zugriff
Auf einer Maschine mit Registry-Zugriff ("Docker-Host") jedes Image herunterladen und als Tarball exportieren:
docker pull <image>
docker save -o <name>.tar <image>
Tarball auf jeden Worker-Node übertragen, auf dem IDIAL-Pods laufen können:
scp <name>.tar <user>@<worker>:~/
Auf jedem Worker-Node importieren:
sudo ctr -n k8s.io images import <name>.tar --base-name <image>
Import verifizieren:
sudo ctr -n k8s.io images ls | grep <name>
Alle Deployment-Manifeste verwenden imagePullPolicy: Never — ein Pod
schlägt mit ErrImageNeverPull fehl, sobald ein Image auf dem Node, auf dem
er geplant wurde, nicht per ctr import vorhanden ist. Images müssen daher
auf allen Worker-Nodes importiert werden, nicht nur auf einem.
3. Deployment
3.1 Manifeste beziehen
git clone <repository-url>
cd <repository>/manifests/sqlite-internal-pki
3.2 Secrets erzeugen
Secret-Werte werden nie in einer Datei committet oder verteilt. Vor dem ersten Deployment einmalig:
./generate-secrets.sh
Das Skript ist idempotent — bereits vorhandene Secrets werden
übersprungen, nicht überschrieben. Das ist notwendig: SECRET_KEY und
IDIAL_APP_MASTER_KEY dürfen nach dem ersten erfolgreichen App-Start nie
neu generiert werden, sonst werden aktive Web-Sessions ungültig bzw.
bereits verschlüsselte App-Daten unlesbar. Erneutes Ausführen des Skripts
(z. B. nach einem Redeploy, bei dem nur die Secrets fehlen) ist dadurch
gefahrlos möglich.
01-secrets.yaml.example dokumentiert nur die Struktur (welche Secrets,
welche Keys) und wird von kubectl apply -f <verzeichnis> nicht
mitgenommen (Dateiendung .example).
3.3 Anwenden
kubectl apply -f .
Reihenfolge spielt keine Rolle — jedes Manifest trägt seine Abhängigkeiten
über initContainer-Wartschleifen selbst (siehe 3.4).
3.4 Startreihenfolge & erwartetes Verhalten
- Schema-Install läuft als
initContainerauf demidial-Deployment selbst, vor dem Hauptcontainer. idial-web-backendwartet perinitContainer, bisidial:5000erreichbar ist.- Der PKI-Bootstrap (
pki-copy-files-from-webdav→pki-db-schema-sql/pki-config-editor→cis-server/cf-server/pgw) läuft über eine Kette voninitContainer-Wartschleifen auf reale Artefakte in gemeinsam gemounteten Volumes. - Erwartetes, unkritisches Verhalten:
cis-serverkann in den ersten 1–2 Minuten nach dem Deploy kurzzeitig inCrashLoopBackOfflaufen, bispki-db-schema-sqlabgeschlossen ist — Kubernetes startet ihn danach automatisch neu, kein manueller Eingriff nötig.
Wichtig bei node-lokalem Storage (z. B. local-path-provisioner) auf
einem Multi-Node-Cluster: mehrere ReadWriteOnce-PVCs im PKI-Stack
werden von verschiedenen Jobs/Deployments gemeinsam gemountet. Da eine
solche PVC beim ersten Gebrauch an genau einen Node bindet, müssen alle
Objekte, die dieselben PVCs teilen, auf demselben Node landen. Die
betroffenen sechs Objekte (13-pki-setup-jobs.yaml,
14-pki-cis-server.yaml, 15-pki-cf-server-pgw.yaml) tragen dafür
nodeSelector: { kubernetes.io/hostname: k8s-worker } — den Hostnamen vor
dem Deployment auf einen tatsächlich existierenden Node dieses Clusters
anpassen, oder auf eine RWX-fähige StorageClass wechseln, um das
Node-Pinning ganz zu entfernen.
4. Verifizierung
kubectl get pods -n idial -w
Erwarteter Endzustand (kann je nach Cluster-Geschwindigkeit 1–3 Minuten
dauern): alle Deployments 1/1 Running, alle Jobs Completed.
kubectl get pods -n idial
kubectl get jobs -n idial
kubectl get pvc -n idial
Core-API-Erreichbarkeit cluster-intern prüfen:
kubectl -n idial run curl-test --rm -it --image=curlimages/curl --restart=Never \
-- curl -sk https://idial:5000/
5. Externer Zugriff
5.1 Ingress-Voraussetzung
16-ingress.yaml setzt einen bereits installierten ingress-nginx
(IngressClass: nginx) voraus — der Controller selbst wird von diesem
Manifest-Satz nicht mit installiert.
5.2 Pfad-Übersicht
| Pfad | Service | Port | Hinweis |
|---|---|---|---|
/ | idial-web-backend | 5555 | UI, beansprucht die gesamte URL |
/core-api | idial | 5000 | Core-REST-API |
/pgw | pgw | 8080 | Protocol Gateway (HTTP) |
/files | files | 8000 | Interner PKI-Artefaktserver |
Wichtig: für die Core-API ausschließlich /core-api verwenden, nicht
/api — idial-web-backend besitzt eigene interne Routen unter
/api/... (vom Browser-JS aufgerufen). Ein Ingress-Pfad /api für Core
würde mit diesen Routen kollidieren.
5.3 Zugriff testen
http://<node-ip>:<ingress-http-nodeport>/
https://<node-ip>:<ingress-https-nodeport>/
idial und idial-web-backend terminieren TLS intern selbst
(selbstsigniertes Zertifikat) — der Ingress ist entsprechend mit
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" und
nginx.ingress.kubernetes.io/proxy-ssl-verify: "off" konfiguriert.
6. Fehlerbehebung
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
ErrImageNeverPull | Image auf diesem Node nicht importiert | ctr -n k8s.io images import auf dem betroffenen Node nachholen (Abschnitt 2.2) |
Pod bleibt dauerhaft Pending, FailedScheduling: node(s) didn't match PersistentVolume's node affinity | Gemeinsam genutzte PVCs sind auf unterschiedliche Nodes gebunden | nodeSelector in den betroffenen Manifesten prüfen/anpassen (Abschnitt 3.4) |
Pod bleibt Pending, Events zeigen untolerated taint {node.kubernetes.io/unreachable} | Ein oder mehrere Nodes sind aktuell nicht erreichbar | kubectl get nodes prüfen; Kubelet/Netzwerk auf dem betroffenen Node untersuchen — kein Deployment-Problem |
cis-server kurz nach dem Deploy in CrashLoopBackOff | Erwartet, siehe Abschnitt 3.4 | Kein Eingriff nötig, löst sich automatisch sobald pki-db-schema-sql fertig ist |
idial-web-backend: CRITICAL ERROR: Master key has incorrect length: 48 bytes (expected: 32) | IDIAL_APP_MASTER_KEY wurde mit openssl rand -hex statt -base64 erzeugt | Secret mit openssl rand -base64 32 neu erzeugen (nur vor dem ersten produktiven Start — siehe Warnung in 3.2) |
Ingress liefert 502 Bad Gateway | Backend terminiert TLS selbst, Ingress spricht per Default HTTP | backend-protocol/proxy-ssl-verify-Annotationen prüfen (bereits Standard in 16-ingress.yaml) |
Browser zeigt {"message":"Missing or invalid API key"} | Core-API unter /api statt /core-api aufgerufen | Ausschließlich /core-api für Core verwenden (Abschnitt 5.2) |
CreateContainerConfigError bei mehreren Deployments | Secrets fehlen | ./generate-secrets.sh ausführen (Abschnitt 3.2) |
error validating ... dial tcp <ip>:6443: connect: connection refused | API-Server auf dem Master nicht erreichbar | Kubelet/containerd-Status auf dem Master prüfen (systemctl status kubelet, journalctl -u kubelet) — unabhängig von diesen Manifesten |
7. Bekannte Einschränkungen
- Nur eine Core-Instanz (
replicas: 1). SQLite erzwingt dies. Für horizontale Skalierung ist die Variante "Externe PostgreSQL" notwendig. EXTERNAL_HOSTin02-configmap.yamlist ein Platzhalter (idial.idial.svc.cluster.local) ohne Aussagekraft außerhalb des Clusters — vor produktivem Gerätebetrieb durch eine tatsächlich erreichbare Adresse ersetzen.- Cores eigener REST-API-Key wird von Core selbst beim ersten Start
generiert und als Datei im
idial-api-key-PVC abgelegt (kein Kubernetes-Secret). Abrufbar per:kubectl exec -n idial deploy/idial -- cat /app/secrets/idial_api_key.txt - Kein TLS auf Ingress-Ebene konfiguriert — bei Bedarf einen eigenen
tls:-Block mit Zertifikat/Secret ergänzen.